Multi-category dot-density maps often work well when the categories cluster geographically. Recent immigrants by country of origin work well for this. ## Data First we grab the immigrant data via cancensus, making use of the CensusMapper API tool to select the regions and variables we need.
#devtools::install_github("mountainmath/cancensus")
library(cancensus)
library(dotdensity)
# options(cancensus.api_key='your_api_key')
regions=list(CMA="59933")
vectors=c("v_CA11N_265","v_CA11N_268","v_CA11N_304","v_CA11N_334","v_CA11N_373","v_CA11N_376","v_CA11N_379","v_CA11N_382")
We choose the categories and colours we want to map and define a convenience function to rename the variables and compute the qantities for the other asian countries that we don’t break out.
categories=c("Americas","Europe","Africa","Philippines","China","India","Other Asian Countries")
colors=c("#7a0177", "#3333cc", "#ff00ff", "#00ffff", "#ff1a1c", "#4dff4a", "#ffff33")
prep_data <- function(geo){
data <- geo@data %>% replace(is.na(.), 0)
data <- rename(data,
total=v_CA11N_265,
Americas=v_CA11N_268,
Europe=v_CA11N_304,
Africa=v_CA11N_334,
Philippines=v_CA11N_376,
China=v_CA11N_379,
India=v_CA11N_382)
data <- mutate(data,`Other Asian Countries` = v_CA11N_373-Philippines-China-India)
geo@data <- data
return(geo)
}
Next we grab the data via cancensus,
data_csd=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CSD') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_5944ecb0017405460024293a4e1245b4.geojson", layer: "OGRGeoJSON"
with 39 features
It has 12 fields
data_ct=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CT') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_2c2ba582c4fbc552cd7375e1cdb94585.geojson", layer: "OGRGeoJSON"
with 457 features
It has 12 fields
data_da=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='DA') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_6b5f32d4a2ad20b88dbcddc4dc419562.geojson", layer: "OGRGeoJSON"
with 3438 features
It has 12 fields
data_db=get_census(dataset = 'CA11', regions=regions,geo_format='sp',labels='short',level='DB')
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_1cc7cbd8f3815230304992d45a56ef3b.geojson", layer: "OGRGeoJSON"
with 15479 features
It has 10 fields
which we then re-aggregate to make sure we don’t miss overall counts due to privacy cutoffs distribute them proportionally among the population.
data_ct@data <- dot_density.proportional_re_aggregate(data=data_ct@data,parent_data=data_csd@data,geo_match=setNames("GeoUID","CSD_UID"),categories=categories,base="Population")
data_da@data <- dot_density.proportional_re_aggregate(data=data_da@data,parent_data=data_ct@data,geo_match=setNames("GeoUID","CT_UID"),categories=categories,base="Population")
data_db@data <- dot_density.proportional_re_aggregate(data=data_db@data,parent_data=data_da@data,geo_match=setNames("GeoUID","DA_UID"),categories=categories,base="Population")
Map
All that’s left to do is to covert our re-aggregated block-level data to dots, using the dot_density.compute_dots function from the dotdensity package and feed it into the dot_density.dots_map function to add them to our basemap.
# 1 dot = 5 immigrants
scale=5
dots.db <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale)
basemap +
# shade unpopulated blocks
# geom_polygon(data=data_db[data_db$Population<=5,],
# aes(long, lat, group = group),
# fill = "#222222", size=0.1,
# color = "#222222") +
scale_colour_manual(values = colors) +
labs(color = "",
title="Immigrants 2006 - 2011",
caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
subtitle = paste0("1 dot = ",scale," people")) +
dot_density.dots_map(dots=dots.db,alpha=0.75,size=0.25)

# save image for later
ggsave('../images/recent_immigrants.png',width=26,height=26)
LS0tCnRpdGxlOiAiVmlnbmV0dGUgVGl0bGUiCmF1dGhvcjogIlZpZ25ldHRlIEF1dGhvciIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXtWaWduZXR0ZSBUaXRsZX0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KCk11bHRpLWNhdGVnb3J5IGRvdC1kZW5zaXR5IG1hcHMgb2Z0ZW4gd29yayB3ZWxsIHdoZW4gdGhlIGNhdGVnb3JpZXMgY2x1c3RlciBnZW9ncmFwaGljYWxseS4gUmVjZW50IGltbWlncmFudHMgYnkKY291bnRyeSBvZiBvcmlnaW4gd29yayB3ZWxsIGZvciB0aGlzLgojIyBEYXRhCkZpcnN0IHdlIGdyYWIgdGhlIGltbWlncmFudCBkYXRhIHZpYSBbY2FuY2Vuc3VzXShodHRwczovL2dpdGh1Yi5jb20vbW91bnRhaW5NYXRoL2NhbmNlbnN1cyksIG1ha2luZyB1c2Ugb2YgdGhlIFtDZW5zdXNNYXBwZXIgQVBJIHRvb2xdKGh0dHBzOi8vY2Vuc3VzbWFwcGVyLmNhL2FwaS9DQTExKSB0byBzZWxlY3QgdGhlIHJlZ2lvbnMgYW5kIHZhcmlhYmxlcyB3ZSBuZWVkLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibW91bnRhaW5tYXRoL2NhbmNlbnN1cyIpCmxpYnJhcnkoY2FuY2Vuc3VzKQpsaWJyYXJ5KGRvdGRlbnNpdHkpCiMgb3B0aW9ucyhjYW5jZW5zdXMuYXBpX2tleT0neW91cl9hcGlfa2V5JykKcmVnaW9ucz1saXN0KENNQT0iNTk5MzMiKQp2ZWN0b3JzPWMoInZfQ0ExMU5fMjY1Iiwidl9DQTExTl8yNjgiLCJ2X0NBMTFOXzMwNCIsInZfQ0ExMU5fMzM0Iiwidl9DQTExTl8zNzMiLCJ2X0NBMTFOXzM3NiIsInZfQ0ExMU5fMzc5Iiwidl9DQTExTl8zODIiKQpgYGAKCldlIGNob29zZSB0aGUgY2F0ZWdvcmllcyBhbmQgY29sb3VycyB3ZSB3YW50IHRvIG1hcCBhbmQgZGVmaW5lIGEgY29udmVuaWVuY2UgZnVuY3Rpb24gdG8gcmVuYW1lIHRoZSB2YXJpYWJsZXMgYW5kIGNvbXB1dGUgdGhlIHFhbnRpdGllcyBmb3IgdGhlIG90aGVyIGFzaWFuIGNvdW50cmllcyB0aGF0IHdlIGRvbid0IGJyZWFrIG91dC4KYGBge3J9CmNhdGVnb3JpZXM9YygiQW1lcmljYXMiLCJFdXJvcGUiLCJBZnJpY2EiLCJQaGlsaXBwaW5lcyIsIkNoaW5hIiwiSW5kaWEiLCJPdGhlciBBc2lhbiBDb3VudHJpZXMiKQpjb2xvcnM9YygiIzdhMDE3NyIsICIjMzMzM2NjIiwgIiNmZjAwZmYiLCAiIzAwZmZmZiIsICIjZmYxYTFjIiwgIiM0ZGZmNGEiLCAiI2ZmZmYzMyIpCgpwcmVwX2RhdGEgPC0gZnVuY3Rpb24oZ2VvKXsKICBkYXRhIDwtIGdlb0BkYXRhICU+JSByZXBsYWNlKGlzLm5hKC4pLCAwKQogIGRhdGEgPC0gcmVuYW1lKGRhdGEsCiAgICB0b3RhbD12X0NBMTFOXzI2NSwKICAgIEFtZXJpY2FzPXZfQ0ExMU5fMjY4LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIEV1cm9wZT12X0NBMTFOXzMwNCwKICAgIEFmcmljYT12X0NBMTFOXzMzNCwKICAgIFBoaWxpcHBpbmVzPXZfQ0ExMU5fMzc2LAogICAgQ2hpbmE9dl9DQTExTl8zNzksCiAgICBJbmRpYT12X0NBMTFOXzM4MikKICBkYXRhIDwtIG11dGF0ZShkYXRhLGBPdGhlciBBc2lhbiBDb3VudHJpZXNgID0gdl9DQTExTl8zNzMtUGhpbGlwcGluZXMtQ2hpbmEtSW5kaWEpCiAgZ2VvQGRhdGEgPC0gZGF0YQogIHJldHVybihnZW8pCn0KYGBgCgoKTmV4dCB3ZSBncmFiIHRoZSBkYXRhIHZpYSBgY2FuY2Vuc3VzYCwKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YV9jc2Q9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTEnLCByZWdpb25zPXJlZ2lvbnMsdmVjdG9ycz12ZWN0b3JzLGdlb19mb3JtYXQ9J3NwJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nQ1NEJykgJT4lIHByZXBfZGF0YQpkYXRhX2N0PWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLHZlY3RvcnM9dmVjdG9ycyxnZW9fZm9ybWF0PSdzcCcsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0NUJykgJT4lIHByZXBfZGF0YQpkYXRhX2RhPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLHZlY3RvcnM9dmVjdG9ycyxnZW9fZm9ybWF0PSdzcCcsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0RBJykgJT4lIHByZXBfZGF0YQpkYXRhX2RiPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLGdlb19mb3JtYXQ9J3NwJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nREInKQpgYGAKCndoaWNoIHdlIHRoZW4gcmUtYWdncmVnYXRlIHRvIG1ha2Ugc3VyZSB3ZSBkb24ndCBtaXNzIG92ZXJhbGwgY291bnRzIGR1ZSB0byBwcml2YWN5IGN1dG9mZnMgZGlzdHJpYnV0ZSB0aGVtCnByb3BvcnRpb25hbGx5IGFtb25nIHRoZSBwb3B1bGF0aW9uLgpgYGB7cn0KZGF0YV9jdEBkYXRhIDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2N0QGRhdGEscGFyZW50X2RhdGE9ZGF0YV9jc2RAZGF0YSxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkNTRF9VSUQiKSxjYXRlZ29yaWVzPWNhdGVnb3JpZXMsYmFzZT0iUG9wdWxhdGlvbiIpCmRhdGFfZGFAZGF0YSA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9kYUBkYXRhLHBhcmVudF9kYXRhPWRhdGFfY3RAZGF0YSxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkNUX1VJRCIpLGNhdGVnb3JpZXM9Y2F0ZWdvcmllcyxiYXNlPSJQb3B1bGF0aW9uIikKZGF0YV9kYkBkYXRhIDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2RiQGRhdGEscGFyZW50X2RhdGE9ZGF0YV9kYUBkYXRhLGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiREFfVUlEIiksY2F0ZWdvcmllcz1jYXRlZ29yaWVzLGJhc2U9IlBvcHVsYXRpb24iKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQp0aGVtZV9vcHRzPC1saXN0KHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnZGFyayBncmV5JywgY29sb3VyID0gTkEpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJkYXJrIGdyZXkiLCBzaXplPTEsbGluZXR5cGU9InNvbGlkIixjb2xvcj0iYmxhY2siKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04MCxoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTYwLGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZT0yNSksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NDApLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9NDApLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ImRhcmsgZ3JleSIsIHNpemU9MSxsaW5ldHlwZT0ic29saWQiLGNvbG9yPSJkYXJrIGdyZXkiKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnZGFyayBncmV5Jyxjb2xvciA9ICdkYXJrIGdyZXknKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgzLCAnbGluZXMnKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikpCgpiYXNlbWFwIDwtICAgZ2dwbG90KGRhdGFfY3NkKSArCiAgICBnZW9tX3BvbHlnb24oYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiYmxhY2siLCBzaXplPTAuMSwgY29sb3IgPSAnZ3JleScpICsKICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQobnJvdz0xLG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xNSkpKSArCiAgICBjb29yZF9tYXAocHJvamVjdGlvbj0ibGFtYmVydCIsIGxhdDA9NDksIGxhdD00OS40KSArCiAgICB0aGVtZV9vcHRzCgpgYGAKCgojI01hcApBbGwgdGhhdCdzIGxlZnQgdG8gZG8gaXMgdG8gY292ZXJ0IG91ciByZS1hZ2dyZWdhdGVkIGJsb2NrLWxldmVsIGRhdGEgdG8gZG90cywgdXNpbmcgdGhlIGBkb3RfZGVuc2l0eS5jb21wdXRlX2RvdHNgCmZ1bmN0aW9uIGZyb20gdGhlIFtgZG90ZGVuc2l0eWAgcGFja2FnZV0oKSBhbmQgZmVlZCBpdCBpbnRvIHRoZSBgZG90X2RlbnNpdHkuZG90c19tYXBgIGZ1bmN0aW9uIHRvIGFkZCB0aGVtIHRvCm91ciBiYXNlbWFwLgpgYGB7ciwgZmlnLmhlaWdodD0xMywgZmlnLndpZHRoPTEzLCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRX0KIyAxIGRvdCA9IDUgaW1taWdyYW50cwpzY2FsZT01Cgpkb3RzLmRiIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfZGIsIGNhdGVnb3JpZXMgPSBjYXRlZ29yaWVzLCBzY2FsZT1zY2FsZSkKYmFzZW1hcCArCiMgc2hhZGUgdW5wb3B1bGF0ZWQgYmxvY2tzCiMgIGdlb21fcG9seWdvbihkYXRhPWRhdGFfZGJbZGF0YV9kYiRQb3B1bGF0aW9uPD01LF0sIAojICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCksIAojICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICIjMjIyMjIyIiwgc2l6ZT0wLjEsIAojICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiIzIyMjIyMiIpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKwogIGxhYnMoY29sb3IgPSAiIiwKICAgICAgICAgICAgICAgIHRpdGxlPSJJbW1pZ3JhbnRzIDIwMDYgLSAyMDExIiwKICAgICAgICAgICAgICAgIGNhcHRpb249IlNvdXJjZTogU3RhdENhbiBDZW5zdXMgMjAxNiB2aWEgY2FuY2Vuc3VzICYgQ2Vuc3VzTWFwcGVyLmNhIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCIxIGRvdCA9ICIsc2NhbGUsIiBwZW9wbGUiKSkgKyAKICBkb3RfZGVuc2l0eS5kb3RzX21hcChkb3RzPWRvdHMuZGIsYWxwaGE9MC43NSxzaXplPTAuMjUpCgojIHNhdmUgaW1hZ2UgZm9yIGxhdGVyCmdnc2F2ZSgnLi4vaW1hZ2VzL3JlY2VudF9pbW1pZ3JhbnRzLnBuZycsd2lkdGg9MjYsaGVpZ2h0PTI2KQpgYGAKCg==